package com.google.code.gaestats;

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingDeque;
import org.joda.time.DateTime;

/**
 *
 * @author JaggardM
 */
@Entity
public /*abstract*/ class CounterData implements Iterable<Statistics>
{

	@Id
	private Long id = null;
	private LinkedBlockingDeque<MutableStatistics> data = null;
	DateTime latestTime = null;
	
	/*abstract*/ int calculateDataPosition(DateTime time)
	{
		throw new UnsupportedOperationException();
	}

	/*abstract*/ DateTime normalise(DateTime time)
	{
		throw new UnsupportedOperationException();
	}

	public /*abstract*/ StringBuilder googleJavascriptData()
	{
		throw new UnsupportedOperationException();
	}

	private CounterData()
	{
	}

	CounterData(int size, DateTime latestTime)
	{
		this.data = new LinkedBlockingDeque<MutableStatistics>(size);
		this.latestTime = latestTime;
	}

	protected int getSize()
	{
		if (data == null)
		{
			return 0;
		}
		return data.size();
	}

	protected synchronized void add(Statistics value)
	{
		if (data.remainingCapacity() == 0)
		{
			data.removeFirst();
		}
		data.addLast(new MutableStatistics(value));
	}

	@Override
	public synchronized Iterator<Statistics> iterator()
	{
		return new IteratorImpl(data.iterator());
		//TODO: Can we return the date with this information?
	}

	public void writeStatistics(DateTime time, Statistics stats)
	{
		int pos = calculateDataPosition(time);
		if (pos <= 0)
		{
			add(stats);
			latestTime = normalise(time);
		}
		Iterator<MutableStatistics> it = data.descendingIterator();
		MutableStatistics muteStat = it.next();
		for (int i = 1; i < pos && muteStat != null; i++)
		{
			muteStat = it.next();
		}
		Statistics oldStat = muteStat.get();
		muteStat.set(oldStat.merge(stats));
	}

	private static class IteratorImpl implements Iterator<Statistics>
	{

		private Iterator<MutableStatistics> i;

		public IteratorImpl(Iterator<MutableStatistics> i)
		{
			this.i = i;
		}

		@Override
		public boolean hasNext()
		{
			return i.hasNext();
		}

		@Override
		public Statistics next()
		{
			return i.next().get();
		}

		@Override
		public void remove()
		{
			i.remove();
		}
	}
}
